/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       gateway_base.cpp

	$Header: /game/gateway_base.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "gateway_base.h"

#include "adv_actor_model.h"
#include "adventure_frame.h"
#include "adventure_map_window.h"
#include "adventure_map.h"
#include "adventure_sounds.h"
#include "angle.h"
#include "army.h"
#include "bound_handler.h"
#include "external_string.h"
#include "platform.h"
#include "player.h"
#include "replace_keywords.h"
#include "simple_dialog.h"
#include "sized_int_types.h"
#include "streambuf_operators.h"

extern t_external_string const k_named_object_balloon_help;

namespace
{
	int const k_current_version = 1;
	int const k_current_map_version = 1;
}

// --------------------------------------------------------------------------
// t_gateway_base members
// --------------------------------------------------------------------------
std::string t_gateway_base::get_balloon_help() const
{
	if ( m_identifier.empty() )
	{
		return t_stationary_adventure_object::get_balloon_help();
	}
	else
	{
		return replace_keywords( k_named_object_balloon_help, 
								 "%name", m_identifier,
								 "%type", get_text( "name" ) );
	}
}


std::string t_gateway_base::get_name() const
{
	if ( m_identifier.empty() )
	{
		return t_stationary_adventure_object::get_name();
	}
	else
	{
		return m_identifier;
	}
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
int t_gateway_base::get_version() const
{
	return k_current_version;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_gateway_base::initialize( t_adventure_map& map )
{
	t_stationary_adventure_object::initialize( map );

	// Yes, this works correctly even for whirlpools & subterranean gates (which
	// don't have random names)
	if ( m_identifier.empty() )
		m_identifier = map.get_random_name( get_model_type() );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_gateway_base::read( std::streambuf&						stream, 
					       t_qualified_adv_object_type const&	type,
					       int									version )
{
	return version < 1 || read_string16( stream, m_identifier );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_gateway_base::read_from_map(
	std::streambuf &		stream,
	t_progress_handler *	handler )
{
	int format_version = get< t_uint16 >( stream );
	if ( format_version < 0 || format_version > k_current_map_version )
		return false;

	return format_version < 1 || read_string16( stream, m_identifier );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_gateway_base::write( std::streambuf & stream ) const
{
	return write_string16( stream, m_identifier );
}

namespace
{
	// Scans exit gateway for blocking army
	class t_exit_scanner
	{
		public:
			t_exit_scanner( t_adventure_map & map, t_army *army, t_gateway_base const & gateway_exit, bool prefer_own_army );

			t_army * get_blocking_army( t_adv_map_point & source_trigger_point );
			
		private:
			t_army *				m_blocking_army;
			bool					m_blocking_army_is_ally;
			t_gateway_base const &	m_gateway_exit;
			t_adventure_map &		m_map;
			t_army *				m_original_army;
			bool					m_prefer_own_army;
			t_adv_map_point			m_source_trigger_point;

			void scan_callback( t_adv_map_point const &point, t_adv_map_point const &trigger_point, bool &stop );
	}; 

	t_exit_scanner::t_exit_scanner( t_adventure_map & map, t_army *army, t_gateway_base const & gateway_exit, bool prefer_own_army )
		: m_blocking_army(NULL), m_blocking_army_is_ally( false ),
		  m_gateway_exit( gateway_exit ), m_map( map ), m_original_army( army ),
		  m_prefer_own_army( prefer_own_army )
	{
	}

	t_army * t_exit_scanner::get_blocking_army( t_adv_map_point & source_trigger_point )
	{
		if ( !m_blocking_army )
		{
			t_adv_map_point stopping_point;
			m_gateway_exit.scan_adjacent_spaces( bound_handler( *this, &t_exit_scanner::scan_callback ), stopping_point );
		}

		if ( m_blocking_army )
		{
			source_trigger_point = m_source_trigger_point;
		}

		return m_blocking_army;
	}

	void t_exit_scanner::scan_callback( t_adv_map_point const &point, t_adv_map_point const &trigger_point, bool &stop )
	{
		t_adventure_tile const & tile = m_map.get_adv_tile( point );

		const std::vector<int>& triggers = tile.get_triggers(point.on_bridge);
		for ( std::vector<int>::const_iterator object_iter = triggers.begin(); object_iter != triggers.end(); object_iter++ )
		{
			t_adventure_object & object = m_map.get_adv_object( *object_iter );

			t_army * army = object.get_army();

			// Prefer our own armies first if prefer_own_army is true
			// Prefer armies we can attack ( and move out of the way ) over allies armies
			if ( army )
			{
				if ( m_prefer_own_army )
				{
					if (  army->get_owner_number() == m_original_army->get_owner_number() )
					{
						m_source_trigger_point = trigger_point;
						m_blocking_army = army;
						stop = true;
						return;
					}
					else if (   ( !m_blocking_army || m_blocking_army_is_ally )
							 && ( !same_team( army->get_owner(), m_original_army->get_owner() ) ) )
					{
						m_source_trigger_point = trigger_point;
						m_blocking_army = army;
						m_blocking_army_is_ally = false;
					}
					else if ( !m_blocking_army )
					{
						m_source_trigger_point = trigger_point;
						m_blocking_army = army;
						m_blocking_army_is_ally = true;
					}
				}
				else
				{
					if ( !same_team( army->get_owner(), m_original_army->get_owner() ) )
					{
						m_source_trigger_point = trigger_point;
						m_blocking_army = army;
						stop = true;
						return;
					}
					else if (   ( !m_blocking_army || m_blocking_army_is_ally )
							 && ( army->get_owner_number() == m_original_army->get_owner_number() ) )
					{
						m_source_trigger_point = trigger_point;
						m_blocking_army = army;
						m_blocking_army_is_ally = false;
					}
					else if ( !m_blocking_army )
					{
						m_source_trigger_point = trigger_point;
						m_blocking_army = army;
						m_blocking_army_is_ally = true;
					}
				}
			}
		}
	}
}

// Teleport army.
void t_gateway_base::teleport_army( t_gateway_base const &gateway_exit, t_army *army, 
									t_adventure_frame* frame, 
									bool allow_trading,
									std::string const & success_message,
									t_handler_1< t_army * > success_callback )
{
    /*
    ** Teleport army.
    */
    t_adv_map_point			       gateway_point;
    t_army::t_model const& model = army->get_model();
	t_adventure_map_ptr map = get_map();
	
	t_adv_map_point gateway_exit_trigger_cell;

	if ( !gateway_exit.get_trigger_cell( gateway_exit_trigger_cell ) )
	{
		assert( false );
		gateway_exit_trigger_cell = gateway_exit.get_position();
	}

	// Do we have anywhere to go?
    if (!gateway_exit.find_adjacent_space( gateway_point, *army ))
	{
		// Why not? Do different things for different reasons...
		t_exit_scanner scanner( *map, army, gateway_exit, allow_trading );

		t_army * blocking_army = scanner.get_blocking_army( gateway_exit_trigger_cell );

		if ( blocking_army == NULL )
		{
			if( !army->get_owner()->is_computer() )
			{
				get_adventure_sound( k_adventure_sound_teleport_out )->play( get_sound_volume() );
				ok_dialog( get_text( "Denied_no_room" ), true, get_name() );
				get_adventure_sound( k_adventure_sound_teleport_in )->play( get_sound_volume() );
			}
		}
		else
		{	
			if ( blocking_army->is_visible_to( map->get_player().get_team() ) )
			{
				frame->get_map_window()->center_view( blocking_army->get_position() ) ;
				redraw_windows();
			}

			if ( !allow_trading && same_team( army->get_owner(), blocking_army->get_owner() ) )
			{
				if ( army->get_owner() && !army->get_owner()->is_computer() )
				{
					get_adventure_sound( k_adventure_sound_teleport_out )->play( get_sound_volume() );
					
					std::string text;
					if ( army->get_owner_number() == blocking_army->get_owner_number() )
						text = get_text( "denied_own_army" );
					else
						text = get_text( "denied_allied_army" );

					ok_dialog( text, true, get_name() );
	
					frame->get_map_window()->center_view( army->get_position() ) ;

					get_adventure_sound( k_adventure_sound_teleport_in )->play( get_sound_volume() );
				}
				
				return;
			}

			// Attack or trade
			if (   map->get_player_number() == army->get_owner_number() 
				|| map->get_player_number() == blocking_army->get_owner_number() )
			{
				assert( !map->get_player().is_computer() ); // Defination of local player...
				get_adventure_sound( k_adventure_sound_teleport_out )->play( get_sound_volume() );

				if ( !same_team( army->get_owner(), blocking_army->get_owner() ) )
				{
					if ( map->get_player_number() == army->get_owner_number() )
					{
						ok_dialog( get_text( "start_battle" ), true, get_name() );
					}
					else
					{
						ok_dialog( get_text( "start_battle_attacked" ), true, get_name() );
					}
				}
				get_adventure_sound( k_adventure_sound_teleport_in )->play( get_sound_volume() );
			}


			blocking_army->touch_armies( army, army->get_position(), 
										 get_direction( get_angle( blocking_army->get_position() - gateway_exit_trigger_cell ) ),
										 frame,
										 blocking_army->get_position() );

			if ( !map->is_game_over() )
			{
				if ( map->get_player_number() == army->get_owner_number() )
				{
					frame->get_map_window()->center_view( army->get_position() ) ;
				}
			}

			// Note: Ideally, we'd like to move the army to the blocking army's position at this
			// point; especially with whirlpools, it doesn't make much sense to say that they
			// end up at their starting point after battle. BUT, if the combat was saved, this would
			// be very tricky, so it's better to just leave it. 
		}

		return;
	}


	if( !army->get_owner()->is_computer() )
	{
		if ( !success_message.empty() )
		{
			ok_dialog( success_message, true, get_name() );
		}
		get_adventure_sound( k_adventure_sound_teleport_out )->play( get_sound_volume() );
	}

	///////////////////////////////////////////////////////////////////
	// replay code
	
	army->get_map()->record_teleport_event ( army, army->get_position(), gateway_point );
	
	///////////////////////////////////////////////////////////////////

    // Move army.
	army->move( gateway_point );

	// Call callback to damage army in whirlpools
	success_callback( army );

    army->update_state();

    /*
    ** Update adventure frame.
    */
	frame->update_armies();
	//frame->update();
	if (!army->is_graveyard() && (army->get_owner() != NULL) && !army->get_owner()->is_computer())
    {
		frame->select_army( army );
    }
    frame->reset_pathfinder();
    frame->refresh_path();
	frame->set_view_level( frame->get_map_window()->get_view_level() ); 
	redraw_windows();

	if( !army->get_owner()->is_computer() )
		get_adventure_sound( k_adventure_sound_teleport_in )->play( get_sound_volume() );


	army->trigger_events();

	if ( !map->is_game_over() && army->is_on_map() )
	{
		// Trigger graveyards & the like
		map->activate_trigger( army, gateway_exit_trigger_cell,
							   get_direction( get_angle( gateway_point - gateway_exit_trigger_cell ) ),
							   frame );
	}

	return;
}
